package db

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"yunj/pkg/global"
	"yunj/pkg/yunj/util"

	"gorm.io/gorm"
)

type TableField struct {
}

type DB struct {
	modelInstance            any
	modelInstanceType        reflect.Type // 模型实例类型
	modelGormDB              *gorm.DB
	gormDB                   *gorm.DB
	gormTableName            string              // gorm表名，根据模型结构体实例解析获取
	gormTableColumns         []gorm.ColumnType   // gorm数据表字段信息
	gormLikeTableNameMap     map[string]struct{} // gorm表名模糊匹配map
	Error                    error               // 错误
	RowsAffected             int64               // 影响行数
	tableName                string              // 数据表名，如果设置有后缀，则获取表名时会加上后缀
	tableNameSuffix          string              // 数据表名后缀
	tableFields              []string            // 数据表字段
	tablePrimaryKeys         []string            // 数据表主键
	unionAllGormDB           *gorm.DB
	unionAllTableNameSuffixs []string                                           // union all数据查询表名后缀集合
	unionAllSubqueryHandler  func(suffix string, subquery *gorm.DB) (err error) // union all子查询处理函数
}

func Model(value interface{}) *DB {
	db := &DB{
		modelInstance: value,
	}
	return db
}

// 获取gorm.DB
func (db *DB) GormDB() *gorm.DB {
	if db.gormDB != nil {
		return db.gormDB
	}
	// 获取指定后缀查询对象
	if db.tableNameSuffix != "" {
		db.gormDB = global.MySQL.Table(db.GetTableName())
		return db.gormDB
	}
	db.gormDB = db.getModelGormDB()
	return db.gormDB
}

// 获取union all的gorm.DB
func (db *DB) UnionAllGormDB() (*gorm.DB, error) {
	if db.unionAllGormDB != nil {
		return db.unionAllGormDB, nil
	}
	// 获取union all联表查询对象
	if len(db.unionAllTableNameSuffixs) > 0 {
		gormTableName := db.getGormTableName()
		unionAllSql := ""
		for _, suffix := range db.unionAllTableNameSuffixs {
			subqueryTableName := fmt.Sprintf("%s%s", gormTableName, suffix)
			if !db.IsExistsTableName(subqueryTableName) {
				continue
			}
			// 生成子查询sql
			subquerySql := global.MySQL.ToSQL(func(builder *gorm.DB) *gorm.DB {
				var dryResult []map[string]interface{}
				subquery := builder.Table(subqueryTableName)
				if db.unionAllSubqueryHandler != nil {
					db.unionAllSubqueryHandler(suffix, subquery)
				}
				return subquery.Find(&dryResult)
			})
			if unionAllSql == "" {
				unionAllSql = fmt.Sprintf("(%s)", subquerySql)
			} else {
				unionAllSql += fmt.Sprintf(" UNION ALL (%s)", subquerySql)
			}
		}
		db.unionAllGormDB = global.MySQL.Table("(?) AS temp", global.MySQL.Raw(unionAllSql))
	}
	var err error
	if db.unionAllGormDB == nil {
		err = fmt.Errorf("union all联表查询对象为空！")
		db.setError(err)
	}
	return db.unionAllGormDB, err
}

// 设置错误
func (db *DB) setError(err any) {
	var dbErr error
	switch v := err.(type) {
	case error:
		dbErr = v
	case string:
		dbErr = errors.New(v)
	default:
		dbErr = fmt.Errorf("未知错误！%v", v)
	}
	db.Error = dbErr
}

// 初始化属性
func (db *DB) initAttr() {
	db.gormDB = nil
	db.Error = nil
	db.RowsAffected = 0
	db.tableNameSuffix = ""
	db.unionAllGormDB = nil
	db.unionAllTableNameSuffixs = nil
	db.unionAllSubqueryHandler = nil
}

// 设置表名称后缀（可用于数据分表）
// 此方法会初始化属性（如：重置gormDB）
func (db *DB) SetTableNameSuffix(suffix string) *DB {
	if !util.IsValidTableName(suffix) {
		db.setError(fmt.Errorf("表名称后缀%s不合法", suffix))
		return db
	}
	db.initAttr() // 初始化属性
	db.tableNameSuffix = suffix
	return db
}

// 设置union all查询的表名称后缀
// 此方法会初始化属性（如：重置unionAllGormDB）
func (db *DB) SetUnionAllTableNameSuffixs(suffixs []string, subqueryHandler ...func(suffix string, subquery *gorm.DB) (err error)) *DB {
	for i, suffix := range suffixs {
		if !util.IsValidTableName(suffix) {
			db.setError(fmt.Errorf("表名称后缀%s不合法", suffix))
			return db
		}
		suffixs[i] = suffix
	}
	db.initAttr() // 初始化属性
	db.unionAllTableNameSuffixs = suffixs
	if len(subqueryHandler) > 0 {
		db.unionAllSubqueryHandler = subqueryHandler[0]
	}
	return db
}

// 判断表是否存在
func (db *DB) IsExistsTableName(tableName string) (exists bool) {
	gormLikeTableNameMap := db.getGormLikeTableNameMap()
	// fmt.Printf("111|%v|%v\r\n", tableName, gormLikeTableNameMap)
	if len(gormLikeTableNameMap) == 0 {
		return false
	}
	_, exists = gormLikeTableNameMap[tableName]
	return
}

// 获取gorm表名模糊匹配map
func (db *DB) getGormLikeTableNameMap() map[string]struct{} {
	if db.Error != nil {
		return nil
	}
	if db.gormLikeTableNameMap != nil {
		return db.gormLikeTableNameMap
	}
	gormTableName := db.getGormTableName()
	if gormTableName == "" {
		db.gormLikeTableNameMap = map[string]struct{}{}
		return db.gormLikeTableNameMap
	}
	var likeTableNames []string
	if err := global.MySQL.Raw(fmt.Sprintf("SHOW TABLES LIKE '%s'", gormTableName+"%")).Scan(&likeTableNames).Error; err != nil {
		db.setError(fmt.Sprintf("%s模糊匹配表明查询失败！%v", gormTableName, err))
		return nil
	}
	db.gormLikeTableNameMap = map[string]struct{}{}
	for _, likeTableName := range likeTableNames {
		db.gormLikeTableNameMap[likeTableName] = struct{}{}
	}
	return db.gormLikeTableNameMap
}

// 获取模型实例的gorm.DB
func (db *DB) getModelGormDB() *gorm.DB {
	if db.modelGormDB != nil {
		return db.modelGormDB
	}
	db.modelGormDB = global.MySQL.Model(db.modelInstance)
	return db.modelGormDB
}

// 获取模型实例类型
func (db *DB) getModelType() reflect.Type {
	if db.modelInstanceType == nil {
		t := reflect.TypeOf(db.getModelGormDB().Statement.Model) // 获取目标对象的类型信息（静态类型）
		if t.Kind() == reflect.Ptr {                             // 判断类型是否为指针
			t = t.Elem() // 解指针，获取指针指向的底层元素类型
		}
		db.modelInstanceType = t
	}
	return db.modelInstanceType
}

// 获取gorm数据模型表名
func (db *DB) getGormTableName() string {
	if db.gormTableName != "" {
		return db.gormTableName
	}
	mt := db.getModelType()
	// 1. 优先调用模型的 TableName 方法（自定义表名）
	if method, ok := mt.MethodByName("TableName"); ok {
		// 调用 TableName() string
		result := method.Func.Call([]reflect.Value{reflect.New(mt).Elem()})
		if len(result) > 0 && result[0].Kind() == reflect.String {
			return result[0].String()
		}
	}
	// 2. 无自定义表名时，使用 GORM 命名策略生成
	namingStrategy := db.getModelGormDB().Config.NamingStrategy
	tableName := namingStrategy.TableName(mt.Name()) // 自动处理前缀、复数/单数
	// 3. 处理结构体标签中的 `gorm:"tableName"`（非官方推荐，v1 需手动解析）
	// if tag := t.Tag.Get("gorm"); tag != "" {
	// 	for _, opt := range strings.Split(tag, ";") {
	// 		if strings.HasPrefix(opt, "tableName:") {
	// 			tableName = strings.TrimPrefix(opt, "tableName:")
	// 			break
	// 		}
	// 	}
	// }
	db.gormTableName = tableName
	return db.gormTableName
}

// 获取数据表名
func (db *DB) GetTableName() string {
	if db.tableName == "" {
		db.tableName = db.getGormTableName()
	}
	if db.tableNameSuffix != "" && !strings.HasSuffix(db.tableName, db.tableNameSuffix) {
		db.tableName = fmt.Sprintf("%s%s", db.tableName, db.tableNameSuffix)
	}
	return db.tableName
}

// 获取gorm数据表字段信息
func (db *DB) GetGormTableColumns() (columns []gorm.ColumnType, err error) {
	if db.Error != nil {
		err = db.Error
		return
	}
	if db.gormTableColumns != nil {
		columns = db.gormTableColumns
		return
	}
	tableName := db.GetTableName()
	columnTypes, err := db.getModelGormDB().Migrator().ColumnTypes(tableName)
	if err != nil {
		db.setError(fmt.Errorf("获取数据表%s字段信息失败！%v", tableName, err))
		return
	}
	db.gormTableColumns = columnTypes
	columns = columnTypes
	return
}

// 获取数据表字段
func (db *DB) GetTableFields() (fields []string, err error) {
	if db.Error != nil {
		err = db.Error
		return
	}
	if len(db.tableFields) > 0 {
		fields = db.tableFields
		return
	}

	columns, err := db.GetGormTableColumns()
	if err != nil {
		return
	}
	tableFields := make([]string, 0, len(columns))
	for _, col := range columns {
		tableFields = append(tableFields, col.Name())
	}
	db.tableFields = tableFields
	fields = db.tableFields
	return
}

// 获取主键
func (db *DB) GetTablePrimaryKeys() (pks []string, err error) {
	if db.Error != nil {
		err = db.Error
		return
	}
	if len(db.tablePrimaryKeys) > 0 {
		pks = db.tablePrimaryKeys
		return
	}

	columns, err := db.GetGormTableColumns()
	if err != nil {
		return
	}
	primaryKeys := make([]string, 0, len(columns))
	for _, col := range columns {
		if isPrimaryKey, ok := col.PrimaryKey(); isPrimaryKey && ok {
			primaryKeys = append(primaryKeys, col.Name())
		}
	}
	db.tablePrimaryKeys = primaryKeys
	pks = db.tablePrimaryKeys
	return
}

// 构建条件
func (db *DB) builderWhere(gormDB *gorm.DB, where []interface{}) {
	if len(where) == 0 {
		return
	}
	// 判断是否为：[]interface{}{"state = ? and id = ?",xxx,xxx}
	if w1, ok := where[0].(string); ok && strings.Contains(w1, "?") {
		gormDB.Where(w1, where[1:]...)
		return
	}
	for _, w := range where {
		if wStr, ok := w.(string); ok {
			gormDB.Where(wStr)
			continue
		}
		if wSlice, ok := w.([]interface{}); ok {
			db.builderWhere(gormDB, wSlice)
			continue
		}
	}
}
